/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.list.group;

import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.datalist.ListGroupVo;
import cn.easyplatform.messages.vos.datalist.ListHeaderVo;
import cn.easyplatform.messages.vos.datalist.ListVo;
import cn.easyplatform.type.FieldType;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.ext.Cacheable;
import cn.easyplatform.web.ext.ZkExt;
import cn.easyplatform.web.ext.zul.ComboboxExt;
import cn.easyplatform.web.task.zkex.ListSupport;
import cn.easyplatform.web.task.support.CellCreater;
import cn.easyplatform.web.task.support.SupportFactory;
import cn.easyplatform.web.task.zkex.ComponentBuilderFactory;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class TreeGroup extends Group {

    public static TreeGroup cast(ListSupport support, ListGroupVo group,
                                 ListVo listVo) {
        return createGroup(support, group, listVo, null, 0);
    }

    private static TreeGroup createGroup(ListSupport support, ListGroupVo g,
                                         ListVo listVo, Group parent, int level) {
        TreeGroup group = new TreeGroup(support, listVo, g.getFields());
        group.level = level + 1;
        group.name = g.getName();
        group.title = g.getTitle();
        group.style = g.getStyle();
        if (parent != null)
            parent.addGroup(group);
        else {
            group.rootNode = new Treechildren();
            group.parentNode = group.rootNode;
        }
        if (g.getChild() != null)
            createGroup(support, g.getChild(), listVo, group, group.level);
        return group;
    }

    private Component parentNode;

    private List<Treeitem> groupNodes;

    private List<GroupValue> groupValues;

    private ListRowVo prevRow;

    public TreeGroup(ListSupport support, ListVo listVo, String[] fields) {
        super(support, listVo, fields);
        groupNodes = new ArrayList<Treeitem>();
        groupValues = new ArrayList<GroupValue>();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends Component> T createGroup(ResultHelper rh,
                                               Map<String, Object> evalMap,
                                               Map<String, Component> managedComponents, int showLevel) {
        Object[] ov = new Object[fieldIndexs.length];
        for (int i = 0; i < fieldIndexs.length; i++)
            ov[i] = rh.item().getData()[fieldIndexs[i]] == null ? "" : rh.item().getData()[fieldIndexs[i]];
        if (!Lang.equals(ov, oldValue)) {
            Treeitem ti = new Treeitem();
            //ti.setValue(rh.item());
            ti.setParent(rootNode);
            saveGroupValue(rh.item(), ti, oldValues == null ? ov : oldValues);
            // 需要建立组
            createGroup(ti);
            Treechildren tc = new Treechildren();
            tc.setParent(ti);
            this.parentNode = tc;
            if (child != null) {
                child.rootNode = this.parentNode;
                ((TreeGroup) child).parentNode = this.parentNode;
                child.reset();
            }
            index = 0;
        }
        Component result = null;
        if (child == null) {// 最底层
            Treeitem ti = new Treeitem();
            ti.setParent(parentNode);
            ti.setValue(rh.item());
            createRow(ti, rh.item(), evalMap, managedComponents);
            result = ti;
        }
        if (totalValues != null)
            for (TotalValue t : totalValues) {
                Object val = rh.item().getData()[support.getEntity().isShowRowNumbers() ? t.getIndex() - 1 : t.getIndex()];
                if (val != null)
                    t.caculate(val.toString());
            }
        if (child != null)
            result = child.createGroup(rh, evalMap, managedComponents,
                    showLevel);
        oldValue = ov;
        save();
        if (!rh.hasNext() && parent == null) {
            saveGroupValue(rh.item(), null, oldValues);
            if (child != null)
                ((TreeGroup) child).saveGroupValue(rh.item(), null,
                        child.oldValues);
            refreshGroup(rh.item());
        }
        prevRow = rh.item();
        return (T) result;
    }

    public void createRow(Treeitem ti, ListRowVo rv,
                          Map<String, Object> evalMap,
                          Map<String, Component> managedComponents) {
        Treerow tr = new Treerow();
        setIndex(false);// 往上累加
        int offset = 0;
        if (support.getEntity().isShowRowNumbers()) {
            offset++;
            Treecell label = new Treecell(String.valueOf(index));
            label.setParent(tr);
            managedComponents.put("LIST_INDEX", label);
        }
        Object[] data = rv.getData();
        CellCreater creater = SupportFactory.getCellCreater(support);
        for (int i = 0; i < data.length; i++) {
            ListHeaderVo hv = headers.get(i + offset);
            evalMap.put(hv.getName(), data[i]);
            if (hv.isTotal() && data[i] != null)
                hv.caculate(data[i].toString());
            Component c = creater.createCell(ti, managedComponents, hv, rv, i);
//            if (c.getFirstChild() == null)
//                managedComponents.put(hv.getName(), c);
//            else
//                managedComponents.put(hv.getName(), c.getFirstChild());
            tr.appendChild(c);
        }
        tr.setParent(ti);
        managedComponents.put("self", ti);
    }

    private void createGroup(Treeitem ti) {
        Treerow tr = new Treerow();
        tr.setStyle(style);
        int i = 0;
        if (support.getEntity().isShowRowNumbers()) {
            i++;
            Treecell label = new Treecell();
            tr.appendChild(label);
        }
        for (; i < headers.size(); i++) {
            ListHeaderVo hv = headers.get(i);
            TotalValue tt = null;
            if (totalValues != null) {
                for (TotalValue gv : totalValues) {
                    if (gv.getIndex() == i) {
                        tt = gv;
                        break;
                    }
                }
            }
            Treecell label = new Treecell();
            if (tt != null) {
                label.setAttribute("label",
                        hv.getGroupName() == null ? "" : hv.getGroupName());
                tt.reset();
            }
            if (Strings.isBlank(hv.getGroupStyle())) {
                if (hv.getType() == FieldType.INT
                        || hv.getType() == FieldType.LONG
                        || hv.getType() == FieldType.NUMERIC)
                    label.setStyle("text-align:right");
            } else
                label.setStyle(hv.getGroupStyle());
            tr.appendChild(label);
        }
        ti.appendChild(tr);
    }

    private void saveGroupValue(ListRowVo rv, Treeitem ti, Object[] values) {
        if (prevRow != null)
            rv = prevRow;
        TotalValue[] ov = new TotalValue[totalValues == null ? 0 : totalValues
                .size()];
        for (int i = 0; i < ov.length; i++) {
            TotalValue gv = totalValues.get(i);
            ov[i] = new TotalValue(gv.getTotalType(), gv.getIndex());
            ov[i].setValue(gv.getValue());
        }
        if (ti != null)
            ti.setValue(rv);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            ListHeaderVo lv = support.getHeaders().get(fieldIndexs[i]);
            if (lv.getInstance() != null) {
                if (lv.getInstance() instanceof Combobox) {
                    ComboboxExt cbx = (ComboboxExt) lv.getInstance();
                    sb.append(cbx.getLabel(values[i]));
                } else
                    sb.append(values[i] == null ? "" : values[i].toString());
            } else if (!Strings.isBlank(lv.getComponent())
                    && lv.getComponent().indexOf("<combobox") >= 0) {
                Component comp = Executions.createComponentsDirectly(
                        lv.getComponent(), "zul", null, null);
                if (comp instanceof ComboboxExt) {
                    comp = ComponentBuilderFactory.getZkBuilder(support,
                            (ZkExt) comp, ti).build();
                    if (((Cacheable) comp).isCache())
                        lv.setInstance(comp);
                    comp.detach();
                    ComboboxExt cbx = (ComboboxExt) comp;
                    sb.append(cbx.getLabel(values[i]));
                } else
                    sb.append(values[i] == null ? "" : values[i].toString());
            } else
                sb.append(values[i] == null ? "" : values[i].toString());
        }
        groupValues.add(new GroupValue(ov, index, sb.toString()));
        sb = null;
        if (ti != null) {
            ti.setValue(null);
            groupNodes.add(ti);
        }
    }

    private void refreshGroup(ListRowVo rv) {
        if (groupValues.size() == groupNodes.size())
            saveGroupValue(rv, null, oldValues);
        try {
            Map<String, Object> evalMap = null;
            Map<String, Component> managedComponents = null;
            if (groupEngine != null) {
                evalMap = new HashMap<String, Object>();
                evalMap.put("GROUP_NAME", this.name);
                managedComponents = new HashMap<String, Component>();
                groupEngine.compile();
            }
            for (int i = 0; i < groupNodes.size(); i++) {
                Treeitem ti = groupNodes.get(i);
                if (evalMap != null) {
                    evalMap.clear();
                    managedComponents.clear();
                }
                GroupValue gvs = groupValues.get(i + 1);
                Component treerow = ti.getFirstChild();

                Treecell cell = (Treecell) treerow.getChildren().get(0);
                if (support.getEntity().isShowGroupCount())
                    cell.setLabel(gvs.getName() + "(" + gvs.getIndex() + ")");
                else
                    cell.setLabel(gvs.getName());

                if (support.getEntity().isShowRowNumbers() && evalMap != null) {
                    evalMap.put("LIST_INDEX", gvs.getIndex());
                    managedComponents.put("LIST_INDEX", treerow.getChildren()
                            .get(0));
                }

                for (TotalValue gv : gvs.getTotal()) {
                    ListHeaderVo hv = headers.get(gv.getIndex());
                    cell = (Treecell) treerow.getChildren().get(gv.getIndex());
                    String value = gv.getValue(hv.getFormat());
                    cell.setLabel(cell.getAttribute("label") + value);
                    if (evalMap != null) {
                        evalMap.put("SYS_COUNT", gv.getCount());
                        evalMap.put("SYS_TOTAL", gv.getTotal());
                        evalMap.put(hv.getName(), gv.getValue());
                        managedComponents.put(hv.getName(), cell);
                    }
                }
                if (groupEngine != null) {
                    managedComponents.put("self", ti);
                    groupEngine.eval(ti, evalMap, managedComponents);
                }
            }
        } finally {
            if (groupEngine != null)
                groupEngine.destroy();
        }
        if (child != null)
            ((TreeGroup) child).refreshGroup(rv);
    }

    @Override
    public void clear() {
        this.groupNodes.clear();
        this.groupValues.clear();
        prevRow = null;
        if (parent == null) {
            /**
             * clear会导致
             * org.zkoss.zk.ui.WrongValueException: non-negative only
             * 	at org.zkoss.zul.Paging.setTotalSize(Paging.java:100) ~[zul-9.0.0-Eval.jar:9.0.0]
             * 	at org.zkoss.zul.Tree.addVisibleItemCount(Tree.java:332) ~[zul-9.0.0-Eval.jar:9.0.0]
             * 	at org.zkoss.zul.Treechildren.addVisibleItemCount(Treechildren.java:170) ~[zul-9.0.0-Eval.jar:9.0.0]
             * 	at org.zkoss.zul.Treechildren.onChildRemoved(Treechildren.java:159) ~[zul-9.0.0-Eval.jar:9.0.0]
             * 	at org.zkoss.zk.ui.AbstractComponent.removeChild(AbstractComponent.java:1661) ~[classes/:9.0.0]
             * 	at org.zkoss.zk.ui.AbstractComponent$ChildIter.remove(AbstractComponent.java:3536) ~[classes/:9.0.0]
             * 	at java.util.AbstractList.removeRange(AbstractList.java:571) ~[?:1.8.0_201]
             * 	at java.util.AbstractList.clear(AbstractList.java:234) ~[?:1.8.0_201]
             * 	at cn.easyplatform.web.task.zkex.list.group.LevelGroup.clear(LevelGroup.java:151) ~[classes/:?]
             */
            try {
                this.rootNode.getChildren().clear();
                this.parentNode = this.rootNode;
            } catch (Exception e) {
            }
        }
        for (ListHeaderVo hv : headers) {
            if (hv.isTotal())
                hv.reset();
        }
        super.clear();
    }

}
